package com.atguigu.gmall.order.controller;

import com.alibaba.fastjson.JSON;
import com.atguigu.gmall.activity.client.ActivityFeignClient;
import com.atguigu.gmall.cart.client.CartFeignClient;
import com.atguigu.gmall.common.constant.MqConst;
import com.atguigu.gmall.common.result.Result;
import com.atguigu.gmall.common.service.RabbitService;
import com.atguigu.gmall.common.util.AuthContextHolder;
import com.atguigu.gmall.model.cart.CartInfo;
import com.atguigu.gmall.model.order.OrderDetail;
import com.atguigu.gmall.model.order.OrderDetailVo;
import com.atguigu.gmall.model.order.OrderInfo;
import com.atguigu.gmall.model.order.OrderTradeVo;
import com.atguigu.gmall.model.product.SkuInfo;
import com.atguigu.gmall.model.user.UserAddress;
import com.atguigu.gmall.order.service.OrderService;
import com.atguigu.gmall.product.client.ProductFeignClient;
import com.atguigu.gmall.user.client.UserFeignClient;
import org.apache.commons.lang3.StringUtils;
import org.reactivestreams.Publisher;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.util.CollectionUtils;
import org.springframework.web.bind.annotation.*;

import javax.servlet.http.HttpServletRequest;
import java.math.BigDecimal;
import java.util.ArrayList;
import java.util.HashMap;
import java.util.List;
import java.util.Map;
import java.util.concurrent.CompletableFuture;
import java.util.concurrent.ThreadPoolExecutor;

/**
 * @author mqx
 * @date 2020-11-18 11:18:03
 */
@RestController
@RequestMapping("api/order")
public class OrderApiController {

    @Autowired
    private UserFeignClient userFeignClient;

    @Autowired
    private CartFeignClient cartFeignClient;

    @Autowired
    private OrderService orderService;

    @Autowired
    private ThreadPoolExecutor threadPoolExecutor;

    @Autowired
    private ProductFeignClient productFeignClient;

    @Autowired
    private RabbitService rabbitService;

    @Autowired
    private ActivityFeignClient activityFeignClient;
    /*
    a.	收货地址列表

    b.	付款方式：在线付款 {货到付款}

    c.	送货清单

    d.	计算总金额
     */
    @GetMapping("auth/trade")
    public Result trade(HttpServletRequest request){
        //  要获取用户Id
        String userId = AuthContextHolder.getUserId(request);
        //  获取用户收货地址列表
        List<UserAddress> userAddressList = userFeignClient.findUserAddressListByUserId(userId);

        //  送货清单
        List<CartInfo> cartCheckedList = cartFeignClient.getCartCheckedList(userId);
        //  声明一个集合来存储订单明细
        List<OrderDetail> orderDetails = new ArrayList<>();
        //  需要将 cartInfo 中的数据赋值给orderDetail
        for (CartInfo cartInfo : cartCheckedList) {
            //  声明一个订单明细对象
            OrderDetail orderDetail = new OrderDetail();
            //  赋值
            orderDetail.setSkuId(cartInfo.getSkuId());
            orderDetail.setSkuName(cartInfo.getSkuName());
            orderDetail.setImgUrl(cartInfo.getImgUrl());
            orderDetail.setOrderPrice(cartInfo.getSkuPrice());
            orderDetail.setSkuNum(cartInfo.getSkuNum());
            orderDetails.add(orderDetail);
        }

        //  获取购物车满足条件的促销与优惠券信息
        OrderTradeVo orderTradeVo = activityFeignClient.findTradeActivityAndCoupon(orderDetails, Long.parseLong(userId));
        List<OrderDetailVo> orderDetailVoList = null;
        BigDecimal activityReduceAmount = new BigDecimal("0");
        //  分组后的订单明细
        orderDetailVoList = orderTradeVo.getOrderDetailVoList();
        //  返现的总金额
        activityReduceAmount = orderTradeVo.getActivityReduceAmount();

        //  计算总金额
        OrderInfo orderInfo = new OrderInfo();
        //  必须将orderDetailList 这个集合赋值。
        orderInfo.setOrderDetailList(orderDetails);

        orderInfo.setActivityReduceAmount(activityReduceAmount);
        //  计算方法
        orderInfo.sumTotalAmount();
        //  总价格在哪！ orderInfo.getTotalAmount();
        //  这里声明一个map 集合
        HashMap<String, Object> map = new HashMap<>();
        //  存储送货清单
        map.put("detailArrayList",orderDetails);
        map.put("totalAmount",orderInfo.getTotalAmount());
        //  计算件数 两种选择：  一种是算有几种sku , 另一种方式：计算sku的总件数。
        map.put("totalNum",orderDetails.size());
        map.put("userAddressList",userAddressList);
        //  获取流水号并保存
        String tradeNo = orderService.getTradeNo(userId);
        map.put("tradeNo",tradeNo);

        map.put("activityReduceAmount", orderInfo.getActivityReduceAmount());
        map.put("originalTotalAmount", orderInfo.getOriginalTotalAmount());
        map.put("orderDetailVoList", orderDetailVoList);

        map.put("couponInfoList", orderTradeVo.getCouponInfoList());
        //  返回封装好的map 数据
        return Result.ok(map);
    }

    //  保存数据
    //  接收前台传递的Json 数据，并且将其转换对象
    //  http://api.gmall.com/api/order/auth/submitOrder?tradeNo=null
    @PostMapping("auth/submitOrder")
    public Result submitOrder(@RequestBody OrderInfo orderInfo,HttpServletRequest request){

        //  获取用户Id
        String userId = AuthContextHolder.getUserId(request);
        orderInfo.setUserId(Long.parseLong(userId));

        //  获取前台页面传递的流水号
        String tradeNo = request.getParameter("tradeNo");

        //  开始做比较
        boolean flag = orderService.checkTradeCode(userId, tradeNo);
        //  对结果进行判断 flag = true ; 第一次提交，允许保存数据，如果是fasle 表示重复提交
        //  表示比较失败！需要给提示信息！
        if(!flag){
            return Result.fail().message("不能重复提交订单！");
        }

        //  删除流水号
        orderService.deleteTradeNo(userId);
        //  这个位置验证库存：用户购买的每个商品都需要验证！
        //  声明一个字符串集合来记录是否有消息提示
        List<String> errorList = new ArrayList<>();
        //  记录异步编排的集合
        List<CompletableFuture> futureList = new ArrayList<>();
        //  获取订单明细集合
        List<OrderDetail> orderDetailList = orderInfo.getOrderDetailList();
        if (!CollectionUtils.isEmpty(orderDetailList)){
            //  循环遍历
            for (OrderDetail orderDetail : orderDetailList) {
                //  调用服务层的方法 skuId ,skuNum 如果有库存，则返回true，没有则返回false
                //                boolean res =  orderService.checkStock(orderDetail.getSkuId(),orderDetail.getSkuNum());
                //                //  表示没有足够的库存！
                //                if (!res){
                //                    return Result.fail().message(orderDetail.getSkuName()+"没有足够的库存！");
                //                }
                //                //  验证价格：
                //                //  获取skuInfo orderDetail.getOrderPrice() == skuInfo.getPrice();
                //                //  要想测试这个case, 首先你的需要更改redis+mysql！    只改数据库不改缓存！
                //                //  管理缓存，如果数据库发送了变化，则必须先将缓存删除，这样用户下次查询的时候就一定是从数据库中获取最新的数据了！
                //                SkuInfo skuInfo = productFeignClient.getSkuInfo(orderDetail.getSkuId());
                //
                //                //  价格比较 5.00   ==  5   5.00 == 5.00 orderDetail.getOrderPrice() == skuInfo.getPrice()
                //                if (orderDetail.getOrderPrice().compareTo(skuInfo.getPrice())!=0){
                //                    //  更新购物车的价格
                //                    //  loadCartCache(userId); 自动更新购物车中的价格！
                //                    cartFeignClient.loadCartCache(userId);
                //                    return Result.fail().message(orderDetail.getSkuName()+"价格有变动!");
                //                }

                //  查询用户的身份，查看商品的优惠劵，满减。。。。
                //  开多线程去操作！
                CompletableFuture<Void> checkStockCompletableFuture = CompletableFuture.runAsync(() -> {
                    //  验证库存
                    boolean res = orderService.checkStock(orderDetail.getSkuId(), orderDetail.getSkuNum());
                    //  表示没有足够的库存！
                    if (!res) {
                        errorList.add(orderDetail.getSkuName() + "没有足够的库存！");
                    }
                }, threadPoolExecutor);
                //  将这个验证库存的异步编排添加到集合
                futureList.add(checkStockCompletableFuture);
                //  验证价格：
                CompletableFuture<Void> skuPriceCompletableFuture = CompletableFuture.runAsync(() -> {
                    //  获取实时价格
                    SkuInfo skuInfo = productFeignClient.getSkuInfo(orderDetail.getSkuId());
                    //  比较价格
                    if (orderDetail.getOrderPrice().compareTo(skuInfo.getPrice())!=0){
                        //  更新购物车的价格
                        //  loadCartCache(userId); 自动更新购物车中的价格！
                        cartFeignClient.loadCartCache(userId);
                        errorList.add(orderDetail.getSkuName()+"价格有变动!");
                    }
                }, threadPoolExecutor);

                //  将这个验证库存的异步编排添加到集合
                futureList.add(skuPriceCompletableFuture);
            }
        }
        //  多任务组合：
        //  将所有任务都放入集合了。需要将这个集合转成数组。
        CompletableFuture.allOf(futureList.toArray(new CompletableFuture[futureList.size()])).join();
        //  获取errorList 中的数据 判断当前这个errorList 集合是否为空！
        if (errorList.size()>0){
            //  说验证失败了！ 提示信息
            return Result.fail().message(StringUtils.join(errorList,","));
        }
        //  调用保存方法
        Long orderId =  orderService.saveOrderInfo(orderInfo);
        //  发送消息 电脑：24 小时， 布娃娃 10 小时。
        rabbitService.sendDelayMessage(MqConst.EXCHANGE_DIRECT_ORDER_CANCEL,MqConst.ROUTING_ORDER_CANCEL,orderId,MqConst.DELAY_TIME);
        //  http://payment.gmall.com/pay.html?orderId=5
        return Result.ok(orderId);
    }

    //  根据订单Id 查询订单明细
    @GetMapping("inner/getOrderInfo/{orderId}")
    public OrderInfo getOrderInfo(@PathVariable Long orderId){

        return orderService.getOrderInfo(orderId);
    }
    //  http://localhost:8204/api/order/orderSplit?orderId=xxx&wareSkuMap=xxx
    @PostMapping("orderSplit")
    public String orderSplit(HttpServletRequest request){
        //  获取传递过来的参数
        String orderId = request.getParameter("orderId");
        String wareSkuMap = request.getParameter("wareSkuMap");

        //  调用拆单方法
        List<OrderInfo> subOrderInfoList = orderService.orderSplit(Long.parseLong(orderId),wareSkuMap);
        //  声明一个集合来存储map
        List<Map> maps = new ArrayList<>();
        //  循环遍历当前集合组成json 字符串
        for (OrderInfo orderInfo : subOrderInfoList) {
            //  orderInfo 转换成map
            Map map = orderService.initWareOrder(orderInfo);
            maps.add(map);
        }
        // 返回子订单集合json 字符串
        return JSON.toJSONString(maps);

    }

    //  秒杀订单接口
    @PostMapping("inner/seckill/submitOrder")
    public Long submitOrder(@RequestBody OrderInfo orderInfo){
        //  保存订单，并返回订单Id
        return orderService.saveOrderInfo(orderInfo);
    }
}
